#include "net.h"

void handleSigchld(int signo);
int printClient(struct sockaddr_in cin);
void clientDataHandle(void *arg, sqlite3 *db);
void do_register(int clientfd, MSG *msg, sqlite3 *db);
int do_login(int clientfd, MSG *msg, sqlite3 *db);
int do_query(int clientfd, MSG *msg, sqlite3 *db);
int do_history(int clientfd, MSG *msg, sqlite3 *db);

//msg
MSG msg;
int main(int argc, const char *argv[])
{
	//1.socket
	int socketfd = socket(AF_INET, SOCK_STREAM, 0);
	if (socketfd < 0)
	{
		perror("socket");
		return -1;
	}
	//2.bind
	int b_reuse = 1;
	setsockopt (socketfd, SOL_SOCKET, SO_REUSEADDR, &b_reuse, sizeof (int));

	struct sockaddr_in sin;
	bzero(&sin, sizeof(sin));
	sin.sin_family = AF_INET;
	sin.sin_port = htons(SERVER_PORT);
	sin.sin_addr.s_addr = htonl(INADDR_ANY);
	int bindret = bind(socketfd, (struct sockaddr *)&sin, sizeof(sin));
	if (bindret < 0)
	{
		perror("bind");
		return -1;
	}
	//3.listen
	int listenret = listen(socketfd, BACKLOG);
	if (listenret < 0)
	{
		perror("listen");
		return -1;
	}
	//4.signal to recycle child fork
	signal(SIGCHLD, handleSigchld);
	//5.open sqlite3
	sqlite3 *db;
	if (sqlite3_open(DATABASE, &db) != SQLITE_OK)
	{
		printf("%s\n", sqlite3_errmsg(db));
		return -1;
	}
	else
	{
		printf("open database %s successfully!\n", DATABASE);
	}
	//6.create table usr and record
	char *errmsg;
	if (sqlite3_exec(db, "create table if not exists usr(name TEXT PRIMARY KEY, password TEXT);", NULL, NULL, &errmsg) != SQLITE_OK)
	{
		printf("%s\n", errmsg);
		return -1;
	}
	if (sqlite3_exec(db, "create table if not exists record(name TEXT, date TEXT, word TEXT);", NULL, NULL, &errmsg) != SQLITE_OK)
	{
		printf("%s\n", errmsg);
		return -1;
	}

	int clientfd = -1;
	struct sockaddr_in cin;
	bzero(&cin, sizeof(cin));
	int clen = sizeof(cin);
	while (1)
	{
		//7.accept
		clientfd = accept(socketfd, (struct sockaddr *)&cin, &clen);
		if (clientfd < 0)
		{
			perror("accept");
			return -1;
		}

		//8.fork
		pid_t pid = fork();
		if (pid < 0)
		{
			perror("fork");
			return -1;
		}
		else if (pid == 0)//child fork:handle client data
		{
			close(socketfd);
			if (printClient(cin) == -1)
				exit(-1);
			clientDataHandle(&clientfd, db);
		}
		else//father fork:handle client requests
		{
			close(clientfd);
		}
	}

	close(socketfd);
	return 0;
}

//to recycle child fork
void handleSigchld(int signo)
{
	wait(NULL);
}
//print client ip and port
int printClient(struct sockaddr_in cin)
{
	char ipv4_addr[16];
	if (!inet_ntop(AF_INET, (void *)&cin.sin_addr, ipv4_addr, sizeof(cin)))
	{
		perror("inet_ntop");
		return -1;
	}
	printf("Client(%s:%d) is connected!\n", ipv4_addr, ntohs(cin.sin_port));
	return 0;
}

void clientDataHandle(void *arg, sqlite3 *db)
{
	int clientfd = *(int *)arg;
	printf("Child fork handling clientfd:%d\n", clientfd);
	//if client quit,recv() returns 0, then process will exit	
	while (recv(clientfd, &msg, sizeof(msg), 0))
	{
		switch (msg.type)
		{
		case R:
			do_register(clientfd, &msg, db);
			break;
		case L:
			do_login(clientfd, &msg, db);
			break;
		case Q:
			do_query(clientfd, &msg, db);
			break;
		case H:
			do_history(clientfd, &msg, db);
			break;
		}
	}
	close(clientfd);
	exit(0);
}

void do_register(int clientfd, MSG *msg, sqlite3 *db)
{
	char *errmsg;
	char sql[128];
	sprintf(sql, "insert into usr values('%s', '%s');", msg->name, msg->data);
	printf("sql:%s\n", sql);
	if (sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)
	{
		printf("%s\n", errmsg);
		strcpy(msg->data, "user already exists!");
	}
	else
	{
		printf("register successfully!\n");
		strcpy(msg->data, "ok");
	}
	if (send(clientfd, msg, sizeof(MSG), 0) < 0)
	{
		perror("send");
		return;
	}
	return;
}

int do_login(int clientfd, MSG *msg, sqlite3 *db)
{
	char sql[128] = {};
	char **resultp;
	int nrow, ncolumn;
	char *errmsg;
	sprintf(sql, "select * from usr where name='%s' and password='%s';", msg->name, msg->data);
	printf("sql:%s\n", sql);
	if (sqlite3_get_table(db, sql, &resultp, &nrow, &ncolumn, &errmsg) != SQLITE_OK)
	{
		printf("%s\n", errmsg);
		return -1;
	}
	//query successfully,user exists
	if (nrow == 1)
	{
		strcpy(msg->data, "OK");
		if (send(clientfd, msg, sizeof(MSG), 0) < 0)
		{
			perror("send");
			return -1;
		}
		return 1;
	}
	else//nrow == 0, query failed, user doesn't exist
	{
		strcpy(msg->data, "user name or password wrong");
		if (send(clientfd, msg, sizeof(MSG), 0) < 0)
		{
			perror("send");
			return -1;
		}
	}

	return 0;
}

int do_searchword(int clientfd, MSG *msg, char *word)
{
	FILE *fp;
	if ((fp = fopen("dict.txt", "r")) == NULL)
	{
		perror("fopen");
		strcpy(msg->data, "Failed to open dict.txt");
		if (send(clientfd, msg, sizeof(MSG), 0) < 0)
		{
			perror("send");
			return -1;
		}
		return -1;
	}

	int len = strlen(word);
	printf("word:%s, len = %d\n", word, len);
	char readBuf[512] = {};
	int result;
	char *p;
	while (fgets(readBuf, 512, fp) != NULL)
	{
		result = strncmp(word, readBuf, len);
		if (result > 0)
		{
			continue;
		}
		//when readBuf ASCII larger than word,in which means didn't find the word
		if (result < 0)
		{
			break;
		}
		//get the result
		p = readBuf + len;
		while (*p == ' ')
			p++;
		strcpy(msg->data, p);

		fclose(fp);
		return 1;
	}
	fclose(fp);
	return 0;
}

int get_date(char *date)
{
	time_t t;
	struct tm *tp;
	time(&t);
	tp = localtime(&t);
	sprintf(date, "%d-%d-%d %d:%d:%d", tp->tm_year + 1900, tp->tm_mon + 1, tp->tm_mday, tp->tm_hour, tp->tm_min, tp->tm_sec);
	printf("date:%s\n", date);
	return 0;
}

int do_query(int clientfd, MSG *msg, sqlite3 *db)
{
	char word[64];
	int found = 0;
	strcpy(word, msg->data);
	found = do_searchword(clientfd, msg, word);
	char date[128];
	char sql[128];
	char *errmsg;
	if (found == 1)
	{
		get_date(date);
		sprintf(sql, "insert into record values('%s', '%s', '%s');", msg->name, date, word);
		printf("sql:%s\n", sql);
		if (sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)
		{
			printf("%s\n", errmsg);
			return -1;
		}
	}
	else
	{
		strcpy(msg->data, "not found!");
	}

	if (send(clientfd, msg, sizeof(MSG), 0) < 0)
	{
		perror("send");
		return -1;
	}
	return 0;
}

int history_callback(void *arg, int f_num, char **f_value, char **f_name)
{
	int clientfd = *(int *)arg;
	sprintf(msg.data, "%s, %s, %s",f_value[0], f_value[1], f_value[2]);
	if (send(clientfd, &msg, sizeof(msg), 0) < 0)
	{
		perror("send");
		return -1;
	}
	return 0;
}

int do_history(int clientfd, MSG *msg, sqlite3 *db)
{
	char *errmsg;
	if (msg->flag == NORMAL)//normal user
	{

		char sql[128] = {};
		sprintf(sql, "select * from record where name='%s';", msg->name);
		if (sqlite3_exec(db, sql, history_callback, (void *)&clientfd, &errmsg) != SQLITE_OK)
		{
			printf("%s\n", errmsg);
			return -1;
		}
		else
		{
			printf("Query history done!\n");
		}
	}
	else if (msg->flag == SUPER)//super user
	{
		if (sqlite3_exec(db, "select * from record;", history_callback, (void *)&clientfd, &errmsg) != SQLITE_OK)		
		{
			printf("%s\n", errmsg);
			return -1;
		}
		else
		{
			printf("Query all history done!My lord.\n");
		}
	}
	msg->data[0] = '\0';
	if (send(clientfd, msg, sizeof(MSG), 0) < 0)
	{
		perror("send");
		return -1;
	}

	return 0;
}
